/*
 *  TubeScreamer.cpp
 *  
 *
 *  Created by Thomas Fu on 3/24/11.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */

#include "TubeScreamer.h"

using namespace std;

/** Number of samples in 16 bits of resolution */
#define _16BitRes 65536

/** 
 *	Conversion factor from the voltage levels of an iPod signal at the line-in 
 *	port to typical voltage levels produced from an electric guitar 
 */
#define LINE_IN_TO_GUITAR_LEVEL 1 / 32767.0

/** Nominal maximum voltage to scale signals to for processing consistency */
#define NOMINAL_MAX_VOLTAGE  5

/** 
 * Maximum multiplier for the gain of the distorted signal in the Tube Screamer
 * emulator
 */
#define TUBE_SCREAM_GAIN_MIN 1

/** 
 * Minimum multiplier for the gain of the distorted signal in the Tube Screamer
 * emulator
 */
#define TUBE_SCREAM_GAIN_MAX 700

/* 
 * Default constructor for the TubeScreamer effect with middle values for 
 * volume, gain, and tone
 */
TubeScreamer::TubeScreamer()
{	
	gain = 50;
	tone = 50;
	volume = 50;
	dirty = 50;

	transferFunction = DistortionFastNN();
	highPass = HPFilter();
	lowPass = LPFilter();
	highShelfFilter = ShelvingFilter();
}

/*
 * Constructor for the TubeScreamer effect that specifies values for volume,
 * gain, and tone.
 *
 * @param volume A value between 0 and 100 specifying the amount of 
 * post-overdrive amplification of the signal
 *
 * @param gain A value between 0 and 100 specifying the amount of distortion
 * to be added to the signal
 *
 * @param tone A double between 0 and 100 that changes the output tone of 
 * the Tube Screamer (0 corresponds to a more muddy, bassy sound whereas
 * 100 corresponds to a more trebly sound).
 *
 * @param dirty A double between 0 and 100 that specifies the relative level
 * of the distorted signal to the clean signal
 */
TubeScreamer::TubeScreamer(double volume, double gain, double tone,
						   double dirty)
{	
	this->gain = gain;
	this->tone = tone;
	this->volume = volume;
	this->dirty = dirty;
	
	transferFunction = DistortionFastNN();
	highPass = HPFilter();
	lowPass = LPFilter();
	highShelfFilter = ShelvingFilter();
}

/* Destructor for the TubeScreamer class */
TubeScreamer::~TubeScreamer()
{
	/*delete transferFunction;
	delete highPass;
	delete lowPass;
	delete highShelfFilter;*/
}

/*
 * Function that processes the data from the input buffer through an 
 * emulator of the Ibanez Tube Screamer pedal to produce an overdriven
 * version of the input buffer at the output buffer.
 *
 * @param outputBuffer The buffer of data to be written to the output of the 
 * program (currently the speakers of the computer)
 *
 * @param inputBuffer The buffer of data read from the input to the program
 * (either the line-in port or the built-in microphone)
 *
 * @param data A pointer to a processData struct cast as a void*
 *
 * @return An integer encoding information about the successful execution of 
 * this function. A zero indicates successful execution with no problems.
 */
int TubeScreamer::processSound(void *outputBuffer, void *inputBuffer, 
													void *data)
{
	
	unsigned int nBufferFrames = ((processData*)data)->nBufferFrames;
	
	for(unsigned int i = 0; i < nBufferFrames; i++)
	{
		
		// High pass filter the signal
		double hpFiltered = advanceModule(highPass, ((double*)inputBuffer)[i] * LINE_IN_TO_GUITAR_LEVEL);
		
		
		// Soft-clip the signal via nonlinear lookup table
		int index = (int)(((hpFiltered * gain + NOMINAL_MAX_VOLTAGE) / 
		(2 * NOMINAL_MAX_VOLTAGE)) * (_16BitRes + 1) + 0.5);
		
		if(index < 1)
			index = 1;
		else if(index > _16BitRes + 1)
			index = _16BitRes + 1;
		
		double *input = new double;
		*input = index / (double)_16BitRes * 0.9 + 0.05;
		setInputs(transferFunction, input);
		generateOutput(transferFunction);

		// Mix clipped signal with original signal
		double outputVal = (*(transferFunction->outputValues) - 0.05) * (5 / 0.9) * (dirty / 100) - 2.5
			+ ((double*)inputBuffer)[i] * LINE_IN_TO_GUITAR_LEVEL;

		// Hard clip at 2.5 volts
		if(outputVal >= 2.5)
			outputVal = 2.5;
		else if(outputVal <= -2.5)
			outputVal = -2.5;

		// Scale back to 1V output for computer speakers
		outputVal /= 5;

		double temp = advanceModule(lowPass, outputVal);
		//temp = advanceModule(highShelfFilter,temp);
		
		// Adjust volume
		temp *= volume / 25.0;
		((double*)outputBuffer)[i] = temp;
	}
	
	return 0;
}